#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "alloc.h"

fb* ch;

/*Initialise la liste des zones libres avec une seule zone correspondant à l'ensemble du tableau mem_heap. Lorsqu’on appelle mem_init alors que
 des allocations et libérations ont déjà été effectuées, l’ensemble des
 structures de données est réinitialisé.*/
void mem_init(){
        //Allocation du descripteur.
        ch= (fb*)mem_heap;      
        ch->size=HEAP_SIZE;
        ch->next=NULL;
}

/*Cette procédure reçoit en paramètre la taille size de la zone à allouer
 (cette taille est arrondie au plus petit multiple de la taille du
 descripteur de zone supérieur ou égal à size). Elle retourne un pointeur
 vers la zone allouée et NULL en cas d’allocation impossible.*/
void *mem_alloc(size_t size){
        fb* courant= ch;
        int estTrouvee=0;
        void* p=NULL;
       
        //arondi à la taille du descripteur supérieur:
        if(size % sizeof(fb)) size = size - (size % sizeof(fb)) + sizeof(fb);
        //Recherche de place
        while (!estTrouvee && courant!= NULL){
                if (courant->size-sizeof(fb) >= size){
          //Si taille de la place >= taille demandé+ descripteur.
                        estTrouvee=1;
                }else{
                        courant= courant->next;
                }      
        }
       
        if (estTrouvee){
                //diminuer la taille disponible
                courant->size-=size;
               
                //recuperer le pointeur de l'espace alloue
                p=(void*)((char*)courant+courant->size-size);
        }
        return p;
}

/* Supprime l element d'adresse aSupr. */
void liberer_desc(void *aSupr){
        fb* courant= ch;
        //On parcours la liste chainée.
        while (courant!= NULL && courant->next!=aSupr){
                courant= courant->next;
        }
        if(courant->next==aSupr){
        //courant le pere de asupr, on actualise le chainage.
                courant->next = ((fb*)aSupr)->next;
        }
}

/* Allouer un descripteur a l'adresse  aCreer de taille size.*/
void allouer_desc(void *aCreer, int size){
        fb* nvDesc;                             //Le nouveau descripteur
        nvDesc= (fb*)((char*) aCreer);
        nvDesc->size=size;
        nvDesc->next=ch;
        ch=nvDesc;              // Insertion en tete de liste.
}

/*Cette procédure reçoit en paramètres l’adresse zone et la taille size
 de la zone à libérer. Elle met à jour la liste des zones libres avec
 fusion éventuelle des zones libres contiguës.*/
void mem_free(void *zone, size_t size){
        fb* courant= ch;
        fb* suivant;
        fb* chevauchant;
        int max=size,inclus=0,chevauche=0,extremite;

         //On parcours la liste chainée
        while (courant!= NULL){
          suivant= courant->next;
          extremite=(int)courant+ courant->size;
          if (extremite >= zone){      
                //Si un descripteur chevauche la zone a supprimer.
                chevauchant = courant;
                chevauche=1; 
          }

          if(courant>=zone && courant<=zone + size){
          //Présent dans la zone.
                inclus=1;  
                // max borne maximale où s'étend la zone a fusionne.
                if(max<((courant-((fb*)zone))+courant->size))
                        max= (courant-((fb*)zone))+courant->size;
                liberer_desc(courant);
          }
          courant= suivant;
        }

        if (chevauche==1){//Si descripteur chevauchant la zone:
                        size = size + sizeof(fb) - (size % sizeof(fb) );
                        chevauchant->size +=size ;// max;      
                        //max la plus grande valuer decrite.
        }
        else {// Sinon:
                allouer_desc(zone,max); 
        }
}

/*Cette procédure reçoit en paramètre l’adresse d’une procédure print
 ayant elle- même deux paramètres, l’adresse d’une zone et une taille.
 Cette procédure print doit être utilisée pour afficher à l’écran la
 spécification d’une zone libre.*/
void mem_show(void (*print)(void *zone, size_t size)){
        fb* courant= ch;

         //On parcours la liste chainée
        while (courant!= NULL){
                print (((void*)courant),courant->size);
                courant= courant->next;
        }

}

